Implemented gtk_extended_layout_is_height_for_width() and added another guess algorithm
authorTristan Van Berkom <tristan.van.berkom@gmail.com>
Sun, 11 Apr 2010 02:39:11 +0000 (22:39 -0400)
committerTristan Van Berkom <tristan.van.berkom@gmail.com>
Sun, 11 Apr 2010 02:39:11 +0000 (22:39 -0400)
This patch adds another commented algorythm to find the collective
minimum and natural height for a said width of a horizontally oriented
box (or the opposite). The algorithm works quite well and can be optimized
a bit more - currently its commented because GtkLabel cannot effectively
do width-for-height calculations (doh).

Further, this patch returns an is_height_for_width() preference depending
on the boxes orientation (vertical boxes are width-for-height).

gtk/gtkbox.c

index 144124a6a67faf8e0b7a9e1da0340c9002cb1c79..27c4de118ca0e4546f00363cbcc95771fefbd6e9 100644 (file)
@@ -113,6 +113,7 @@ static void gtk_box_extended_layout_init  (GtkExtendedLayoutIface *iface);
 static void gtk_box_get_desired_size      (GtkExtendedLayout      *layout,
                                            GtkRequisition         *minimum_size,
                                            GtkRequisition         *natural_size);
+static gboolean gtk_box_is_height_for_width (GtkExtendedLayout      *layout);
 static void gtk_box_get_width_for_height  (GtkExtendedLayout      *layout,
                                           gint                    height,
                                           gint                   *minimum_width,
@@ -122,7 +123,7 @@ static void gtk_box_get_height_for_width  (GtkExtendedLayout      *layout,
                                           gint                   *minimum_height,
                                           gint                   *natural_height);
 
-GtkExtendedLayoutIface *parent_extended_layout_iface;
+static GtkExtendedLayoutIface *parent_extended_layout_iface;
 
 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkBox, gtk_box, GTK_TYPE_CONTAINER,
                                   G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
@@ -747,11 +748,20 @@ gtk_box_extended_layout_init (GtkExtendedLayoutIface *iface)
 {
   parent_extended_layout_iface = g_type_interface_peek_parent (iface);
 
+  iface->is_height_for_width  = gtk_box_is_height_for_width;
   iface->get_desired_size     = gtk_box_get_desired_size;
   iface->get_height_for_width = gtk_box_get_height_for_width;
   iface->get_width_for_height = gtk_box_get_width_for_height;
 }
 
+static gboolean 
+gtk_box_is_height_for_width (GtkExtendedLayout      *layout)
+{
+  GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (layout);
+
+  return (private->orientation == GTK_ORIENTATION_HORIZONTAL);
+}
+
 static void
 gtk_box_get_desired_size (GtkExtendedLayout *layout,
                           GtkRequisition    *minimum_size,
@@ -949,23 +959,62 @@ size_fits_for_dimension (GtkBox  *box,
 
 
 static void 
-gtk_box_compute_size_for_awkward_orientation (GtkBox *box,
-                                             gint    avail_size,
-                                             gint   *minimum_size,
-                                             gint   *natural_size)
+gtk_box_bisect_for_size_in_opposing_orientation (GtkBox   *box,
+                                                gboolean  check_natural,
+                                                gint      avail_size,
+                                                gint      floor,
+                                                gint      ceiling,
+                                                gint     *size)
+{
+  if (ceiling - floor <= 1)
+    *size = ceiling;
+  else
+    {
+      gint check_size = floor + (ceiling - floor) / 2;
+
+      if (size_fits_for_dimension (box, avail_size, check_size, check_natural))
+
+       /* If check_size is large enough for box to fit into avail_size, we go on
+        * to check between the given floor and check_size as the new ceiling
+        */
+
+       gtk_box_bisect_for_size_in_opposing_orientation (box, check_natural, avail_size,
+                                                        floor, check_size, size);
+      else
+       gtk_box_bisect_for_size_in_opposing_orientation (box, check_natural, avail_size,
+                                                        check_size, ceiling, size);
+
+    }
+}
+
+static void 
+gtk_box_compute_size_for_opposing_orientation (GtkBox *box,
+                                              gint    avail_size,
+                                              gint   *minimum_size,
+                                              gint   *natural_size)
 {
   gint minimum, natural;
-  
-  /* Find the smallest possible fit in the orientation of 'box' where 'avail_size' is the available
-   * size in the opposing orientation
+  gint min_ceiling, nat_ceiling;
+  gint step = 200;
+
+  /* First a large gap to search inside of
    */
-  for (minimum = 1; !size_fits_for_dimension (box, avail_size, minimum, FALSE); minimum++);
+  for (min_ceiling = (step + 1); !size_fits_for_dimension (box, avail_size, min_ceiling, FALSE); min_ceiling += step);
 
-  /* Starting from the minimum available size, do the same to find the smallest allocation where
-   * all children receive their natural size 
+  /* This will find the minimum sizes by halfing the guesses until they are found
    */
-  for (natural = minimum; !size_fits_for_dimension (box, avail_size, natural, TRUE); natural++);
-  
+  gtk_box_bisect_for_size_in_opposing_orientation (box, FALSE, avail_size,
+                                                  min_ceiling - step,
+                                                  min_ceiling,
+                                                  &minimum);
+
+  /* Basing the natural size on the found minimum, do the same operation for the natural size */
+  for (nat_ceiling = minimum + step; !size_fits_for_dimension (box, avail_size, nat_ceiling, TRUE); nat_ceiling += step);
+  gtk_box_bisect_for_size_in_opposing_orientation (box, TRUE, avail_size,
+                                                  nat_ceiling - step,
+                                                  nat_ceiling,
+                                                  &natural);
+
   if (minimum_size)
     *minimum_size = minimum;
 
@@ -1050,9 +1099,14 @@ gtk_box_get_width_for_height (GtkExtendedLayout *layout,
   GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (layout);
 
   if (private->orientation == GTK_ORIENTATION_VERTICAL)
-/*     gtk_box_compute_size_for_awkward_orientation (box, height, minimum_width, natural_width); */
-    /* Have the base class return the values previously computed by get_desired_size() */
+    {
+#if 0
+    gtk_box_compute_size_for_opposing_orientation (box, height, minimum_width, natural_width); 
+#else
+    /* Have the base class return the values previously computed by get_desired_size() */  
     parent_extended_layout_iface->get_width_for_height (layout, height, minimum_width, natural_width);
+#endif
+    }
   else
     gtk_box_compute_size_for_orientation (box, height, minimum_width, natural_width);
 }
@@ -1067,9 +1121,14 @@ gtk_box_get_height_for_width (GtkExtendedLayout *layout,
   GtkBoxPrivate *private = GTK_BOX_GET_PRIVATE (layout);
 
   if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
-/*     gtk_box_compute_size_for_awkward_orientation (box, width, minimum_height, natural_height); */
-    /* Have the base class return the values previously computed by get_desired_size() */
-     parent_extended_layout_iface->get_height_for_width (layout, width, minimum_height, natural_height); 
+    {
+#if 0
+      gtk_box_compute_size_for_opposing_orientation (box, width, minimum_height, natural_height);
+#else
+      /* Have the base class return the values previously computed by get_desired_size() */
+      parent_extended_layout_iface->get_height_for_width (layout, width, minimum_height, natural_height);
+#endif
+    }
   else
     gtk_box_compute_size_for_orientation (box, width, minimum_height, natural_height);
 }